package client;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;

import serveur.MainServer;

import client.MainClient.CanalUserHandler;

import client.MainClient.CanalAnnonceurHandler;

import message.Message;
import message.MessageChooseFolder;
import message.MessageCloseService;
import message.MessageCloseUser;
import message.MessageCreateFolder;
import message.MessageDeleteFolder;
import message.MessageGetFile;
import message.MessageGetFileFullInFolder;
import message.MessageGetFileInFolder;
import message.MessageOpenService;
import message.MessagePutFile;
import message.ReceiveMessage;
import message.SendMessage;
import common.*;

public class UserClientDelegate implements Runnable {
	
	//Etat de l'automate
	private static final int AttenteUserMode = 9;
	private static final int AttenteConnexion = 10;
	private static final int AttenteClavier = 20;
	private static final int AttenteCommandeUser = 30;
	private static final int AttenteCommandeAdmin = 31;
	private static final int AttenteRetourMessage = 40;
	private static final int Attenteloginpwd = 50;
	private static final int AffichageMenuEnvoieMessage = 60;
	
	private int m_iEtatAuto;
	private Message m_Message;
	private SendMessage m_SendMessage;
	private ReceiveMessage m_ReceiveMessage;
	private String m_IpAddress;
	private Socket m_Socket;
	private boolean m_bModeAdmin;
	
	
	private String m_sUserName;
	private String m_sCurrentServerFolder;
	private CanalAnnonceurHandler m_CanalAnnouceurHandler;
	private MainClient m_Client;
	private AnnonceurClientDelegate m_AnnonceurClientDelegate;
	private ExecutorService m_Pool;
	
	private boolean m_bStop;
	private int m_portflux;
	private String m_sCurrentClientFolder;
	
    private Logger logger = Logger.getLogger(MainClient.class.getName());
	
	public UserClientDelegate(InetAddress mIpAddress, Socket mSocket, int i,boolean bModeAdmin, MainClient client, ExecutorService pool ) {
		m_Socket = mSocket;
		m_bModeAdmin = bModeAdmin;
		m_iEtatAuto = Attenteloginpwd;
		m_Client = client;
		m_Pool = pool;
		m_IpAddress = mIpAddress.toString().substring(1);
		m_sCurrentClientFolder = System.getProperty("user.dir" );
		logger.info("Connection datas : " + m_Socket.toString());
		logger.info("Current directory: "+ m_sCurrentClientFolder);
		setM_bStop(false);
		try {
			m_SendMessage	 = new SendMessage( m_Socket.getOutputStream() );
			m_ReceiveMessage = new ReceiveMessage( m_Socket.getInputStream() );
		} catch (SecurityException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
	}

	boolean sendMessage( Message MessgeToSend )
	{
		boolean bRet = false;
		try {
			m_SendMessage.writeObject( MessgeToSend );
			bRet = true;
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		return bRet;
	}
	
	boolean openCanalAnnouceur(int port)
	{
		boolean bRes = false;
		m_CanalAnnouceurHandler = m_Client.new CanalAnnonceurHandler(m_IpAddress, port, m_Pool);
		m_CanalAnnouceurHandler.createAnnonceurClientDelegate();
		m_AnnonceurClientDelegate = m_CanalAnnouceurHandler.getAnnonceurClientDelegate();
		
		return bRes;
	}
	
	String receiveText()
	{
		String sRet ="";
		try {
			sRet = m_ReceiveMessage.readUTF();
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		return sRet;
	}
	
	void sendText(String sTextToSend)
	{
		String sRet ="";
		try {
			m_SendMessage.writeUTF(sTextToSend);
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
	}
	
	long receivelong()
	{
		long lRet = 0;
		try {
			lRet = m_ReceiveMessage.readLong();
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		return lRet;
	}
	
	boolean receiveMessage()
	{
		boolean bRet = false;
		int messageID = 0;
		try {
			
				m_Message = (Message) m_ReceiveMessage.readObject();
				logger.info(m_Message.getM_sResultatExecution());
				System.out.println(m_Message.getM_sResultatExecution());
				//logger.log(Level.FINE, m_Message.getM_sResultatExecution());
				bRet = m_Message.isM_bResultatExecution();
				if ( bRet == true  )
				{
				messageID = m_Message.getMessageId();
				
				// tri du message en fonction de son ID
				switch (messageID) {
				// MessageOpenService
				case Message.OpenServiceId:
					if ( m_Message.isM_bResultatExecution() == true ){
					m_portflux = m_ReceiveMessage.readInt();
					System.out.println("portflux : " + m_portflux);
					m_sCurrentServerFolder = m_Message.getCurrentFolder();
					}
					break;
					
				// MessageCloseService
				case Message.CloseServiceId:
					break;
					
					// MessageCloseUser
				case Message.CloseUserId:
					break;
					
				// MessageChooseFolder - CD
				case Message.ChooseFolderId:
					MessageChooseFolder cdCommand = ((MessageChooseFolder) m_Message);
					// Recupere le repertoire courant
					m_sCurrentServerFolder = m_Message.getCurrentFolder();
					break;
					
				// MessageCreateFolder - MKDIR
				case Message.CreateFolderId:
					// Rien a faire a la reception du message
					break;
					
				// MessageDeleteFolder - RMDIR
				case Message.DeleteFolderId:
					// Rien a faire a la reception du message
					break;
					
				// MessageGetFile
				case Message.GetFileId:
					break;
					
				// MessageGetFileFullInFolder
				case Message.GetFileFullInFolderId:
					break;
					
				// MessageGetFileInFolder - LS
				case Message.GetFileInFolderId:
					// Recupere la liste des fichiers et l'affiche
					MessageGetFileInFolder lsCommand = ((MessageGetFileInFolder) m_Message);
					File[] result = lsCommand.getM_fileList();
					
					if ( result != null){
						for (int i=0; i< result.length; i++){
							if (result[i].isDirectory() == true ){
								System.out.print("D ");
							} else {
								System.out.print("A ");
							}
							System.out.println(result[i].getName());
						}
					}
					break;
					
				// MessagePutFile
				case Message.PutFileId:
					break;

				default:
					break;
				}
				}
			}
		catch (ClassNotFoundException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		return bRet;
	}
	
	
	/**
	 * Definition des envoie de messages
	 * 
	 */
	
	/**
	 * sendMessageGetFile(String sNomDeFichier)
	 * Param : sNomDeFichier : Nom du fichier à télécharger
	 */
	boolean sendMessageGetFile(String sNomDeFichier)
	{
		boolean bRet = false;
		File rFile = new File ( this.m_sCurrentClientFolder + File.separator + sNomDeFichier );
		openCanalAnnouceur(m_portflux);
		m_Message = new MessageGetFile();
		m_Message.setCurrentFolder(m_sCurrentServerFolder);
		((MessageGetFile) m_Message).setM_sNomFicher( sNomDeFichier );
		bRet = sendMessage(m_Message);
		logger.log(Level.INFO, m_Socket.toString());
		ReceiveFile recvFile = new ReceiveFile (rFile,m_AnnonceurClientDelegate.getM_dataSocket(), m_Socket);
		recvFile.setM_sPathFile(this.m_sCurrentClientFolder + File.separator + sNomDeFichier);
		m_AnnonceurClientDelegate.setM_TransfertMode(CommonFile.ReceiveTransfertMode);
		
		try {
			recvFile.recoitFichier();
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		//bRet = receiveMessage();
		m_AnnonceurClientDelegate.setM_TransfertMode(CommonFile.NoTransfertMode);
		return bRet;
	}
	
	boolean sendMessagePutFile(String sNomDeFichier) {
		boolean bRet = false;
		File rFile = new File ( this.m_sCurrentClientFolder + File.separator + sNomDeFichier );
		openCanalAnnouceur(m_portflux);
		m_Message = new MessagePutFile();
		m_Message.setCurrentFolder(m_sCurrentServerFolder);
		((MessagePutFile) m_Message).setM_sNomFicher(sNomDeFichier);
		bRet = sendMessage(m_Message);
		logger.log(Level.INFO, m_Socket.toString());
		SendFile sendFile = new SendFile (rFile,m_AnnonceurClientDelegate.getM_dataSocket(), m_Socket);
		m_AnnonceurClientDelegate.setM_TransfertMode(CommonFile.ReceiveTransfertMode);
		
		try {
			sendFile.envoieFichier();
		} catch (IOException ex) {
			logger.log(Level.SEVERE,"Unexpected error",ex);
		}
		//bRet = receiveMessage();
		m_AnnonceurClientDelegate.setM_TransfertMode(CommonFile.NoTransfertMode);
		return bRet;
	}
	
	
	boolean sendMessageGetFileInFolder()
	{
		boolean bRet = false;
		// Recupere le repertoire courant avant l'envoi du message LS
		m_Message = new MessageGetFileInFolder();
		m_Message.setCurrentFolder(m_sCurrentServerFolder);
		bRet = sendMessage(m_Message);
		bRet = receiveMessage();
		return bRet;
	}
	
	// Liste le repertoire courant du poste client
	boolean listLocalFolder() {
		// boolean bRet = false; A VERIFIER SI UTILE
		File directory = new File(System.getProperties().get("user.dir").toString());
		System.out.println(directory.getName());
		if (directory.isDirectory()) {
			File[] currentFiles = directory.listFiles();
			if (currentFiles != null){
				for (int i=0; i< currentFiles.length; i++){
					if (currentFiles[i].isDirectory() == true ){
						System.out.print("D ");
					} else {
						System.out.print("A ");
					}
					System.out.println(currentFiles[i].getName());
				}
			}
		}
		return true;
	}
	
	boolean sendMessageChooseFolder(String sRepertoire) {
		boolean bRet = false;
		m_Message = new MessageChooseFolder(sRepertoire);
		// Recupere le repertoire courant avant l'envoi du message CD
		m_Message.setCurrentFolder(m_sCurrentServerFolder);
		// Recupere le nom d'utilisateur avant l'envoi du message CD
		m_Message.setM_sUserName(m_sUserName);
		bRet = sendMessage(m_Message);
		bRet = receiveMessage();
		return bRet;
	}
	
	// Permet de creer un repertoire sur le repertoire courant du serveur
	boolean sendMessageCreateFolder(String sRepertoire) {
		boolean bRet = false;
		m_Message = new MessageCreateFolder(sRepertoire);
		m_Message.setCurrentFolder(m_sCurrentServerFolder);
		bRet = sendMessage(m_Message);
		bRet = receiveMessage();
		return bRet;
	}
	
	// Permet de supprimer un repertoire ou un fichier sur le repertoire courant du serveur
	boolean sendMessageDeleteFolder(String sRepertoire) {
		boolean bRet = false;
		m_Message = new MessageDeleteFolder(sRepertoire);
		m_Message.setCurrentFolder(m_sCurrentServerFolder);
		bRet = sendMessage(m_Message);
		bRet = receiveMessage();
		return bRet;
	}
	
	boolean sendMessageCloseService()
	{
		boolean bRet = false;
		m_Message = new MessageCloseService();
		bRet = sendMessage(m_Message);
		return bRet;
	}
	
	boolean sendMessageCloseUser()
	{
		boolean bRet = false;
		m_Message = new MessageCloseUser();
		bRet = sendMessage(m_Message);
		return bRet;
	}
	
	boolean sendMessageOpenService()
	{
		boolean bRet = false;
		System.out.println("Entrez le login : ");
		affichePrompt();
		String sLogin = client.Clavier.lireString();
		// Stock le nom de l'utilisateur
		m_sUserName = sLogin;
		System.out.println("Entrez le Mot de passe : ");
		affichePrompt();
		String sPassword = client.Clavier.lireString();
		m_Message = new MessageOpenService();
		//((MessageOpenService) m_Message).setM_sLogin(base64encoder.encode(sLogin));
		//((MessageOpenService) m_Message).setM_sPassword(base64encoder.encode(sPassword));
		((MessageOpenService) m_Message).setM_sLogin(sLogin);
		((MessageOpenService) m_Message).setM_sPassword(sPassword);
		((MessageOpenService) m_Message).setM_bResultatExecution(false);
		((MessageOpenService) m_Message).setM_sResultatExecution("Execution non effectuée");
		bRet = sendMessage(m_Message);
		bRet = receiveMessage();
		return m_Message.isM_bResultatExecution();
	}
	
	void affichePrompt(){
		if ( m_bModeAdmin == true ){
			System.out.print("Admin > ");
		}
		else{
			System.out.print("User > ");
		}
	}
	
	void commandMenuUser(){
		System.out.println("Aide :");
		System.out.println("======");
		System.out.println("");
		System.out.println("get [nom de fichier]\t\t: télécharge le fichier ");
		System.out.println("put [nom de fichier]\t\t: envoie le fichier ");
		System.out.println("rm  [nom de fichier]\t\t: supprime le fichier ");
		System.out.println("ls\t\t\t\t: list le répertoire distant ");
		System.out.println("cd    [nom du répertoire]\t: change répertoire distant ");
		System.out.println("dir\t\t\t\t: list le répertoire distant ");
		System.out.println("mkdir [nom du répertoire]\t: créer un répertoire dans le répertoire distant ");
		System.out.println("rmdir [nom du répertoire]\t: supprime un répertoire/fichier dans le répertoire distant ");
		System.out.println("help\t\t\t\t: affiche cette aide ");
		System.out.println("exit\t\t\t\t: clot la session utilisateur");
		System.out.println("quit\t\t\t\t: quitte l'application");
		System.out.println("");
		affichePrompt();
	}
	
	void commandMenuAdmin(){
		System.out.println("*****************************************");
		System.out.println("* Entrer Exit pour quitter");
		System.out.println("*****************************************");
		affichePrompt();
	}
	
	void lectureClavierCommandMenu(){
		String Lecture = client.Clavier.lireString();
		String sCommand[] = Lecture.split("[ ]");
		boolean bCommandOk = false;
		if ( sCommand[0].equalsIgnoreCase("GET") )
		{
			sendMessageGetFile(sCommand[1]);
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("PUT") )
		{
			sendMessagePutFile(sCommand[1]);
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("LS"))
		{
			sendMessageGetFileInFolder();		
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("DIR"))
		{
			listLocalFolder();
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("CD") )
		{
			sendMessageChooseFolder(sCommand[1]);
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("MKDIR"))
		{
			sendMessageCreateFolder(sCommand[1]);
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("RMDIR") )
		{
			sendMessageDeleteFolder(sCommand[1]);
			affichePrompt();
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("HELP") )
		{
			commandMenuUser();
			bCommandOk = true;
		}
		if ( Lecture.equalsIgnoreCase("QUIT") )
		{
			System.out.println(Lecture.toUpperCase());
			sendMessageCloseService();
			m_bStop = true;
			bCommandOk = true;
		}
		if ( sCommand[0].equalsIgnoreCase("EXIT") )
		{
			sendMessageCloseUser();
			receiveMessage();
			m_iEtatAuto = Attenteloginpwd; 
			bCommandOk = true;
		}
		if ( bCommandOk == false ){
			System.out.println("** La commande que vous avez entré n'héxiste pas **");
			commandMenuUser();
		}
	}



	void lectureClavierCommandMenuAdmin(){
		String Lecture = client.Clavier.lireString();
		if ( Lecture.equalsIgnoreCase("QUIT") )
		{
			System.out.println(Lecture.toUpperCase());
			sendMessageCloseService();
			m_bStop = true;
		}
	}
		
	void lectureClavierMainMenu(){
		int LectureInt = client.Clavier.lireInt();
		switch ( LectureInt )
		{
		case 0:
			sendMessageCloseService();
			receiveMessage();
			m_bStop = true;
			break;
		case 1:
			if ( sendMessageOpenService() == true ){
				if ( m_bModeAdmin == true ){
					m_iEtatAuto = AttenteCommandeAdmin;	
				}
				else
				{
					m_iEtatAuto = AttenteCommandeUser;
				}
			}
			break;
		}
	}

	public int getM_iEtatAuto() {
		return m_iEtatAuto;
	}

	public void setM_iEtatAuto(int mIEtatAuto) {
		m_iEtatAuto = mIEtatAuto;
	}
	
	int lectureClavierStartMenu(){
		return client.Clavier.lireInt();
	}
	
	@Override
	public void run() {
		switch ( m_iEtatAuto ) {
		case AttenteUserMode:
			//mainMenu();
	    	switch ( lectureClavierStartMenu() )
			{
			case 1:
				m_bModeAdmin =false;
				break;
			case 2:
				m_bModeAdmin = true;
				break;
			}
	    	m_iEtatAuto = Attenteloginpwd;
			break;
		case Attenteloginpwd:
			logger.log(Level.INFO, "Connection IP : " + m_Socket.toString());
			m_sCurrentClientFolder = System.getProperty("user.dir" );
			System.out.println(m_sCurrentClientFolder);
			if ( sendMessageOpenService() == true ){
				if ( m_bModeAdmin == true ){
					m_iEtatAuto = AttenteCommandeAdmin;
					commandMenuAdmin();
				}
				else
				{
					m_iEtatAuto =AttenteCommandeUser;
					commandMenuUser();
				}
			}
		break;
		case AttenteClavier:
			lectureClavierMainMenu();
		break;
		case AttenteCommandeUser:
			lectureClavierCommandMenu();
		break;
		case AttenteCommandeAdmin:
			lectureClavierCommandMenuAdmin();
		break;
		}
	}

	public void setM_bStop(boolean m_bStop) {
		this.m_bStop = m_bStop;
	}

	public boolean isStop() {
		return m_bStop;
	}
}
